"use strict";

var path = require("path"),
    C = require('./../common.js'),
    Router = require('../router.js').Router,
    RouterWithView = require('../router.js').RouterWithView,
    E = require('./../utils/error.js');

/*
'<base path>': {
    rule: {
        // feature 1, all methods mapped to one action
        '<sub route>': '<controller with relative path>.<action>',

        // feature 2, different methods mapped to different method
        '<sub route>': {
            '<method>': '<controller with relative path>.<action>'
        },

        // feature 3, with middleware
        '<sub route>': {
            '<method>': {
                '<middlware name>': { //middleware options }
            }
        }
    }
}
 */

/**
 *
 * @type {*}
 */

var RuleRouter = RouterWithView.extend({
}, {
    //static members

    mount: function(webModule, route, options, cb) {
        options = C._.extend({
            controllerPath: 'controllers',
            modelPath: 'models'
        }, options);

        var self = this,
            engine = webModule.app.engine,
            routeRule;

        function createRouter(req, res, next) {
            try {
                var actionKey = req.method.toLowerCase() + ':' + req.path,
                    actionInfo = webModule.routes[actionKey];

                if (!actionInfo) {
                    actionInfo = webModule.routes['all:' + req.path];
                }

                if (!actionInfo) {
                    return Router.onError(webModule, req, res, C.HttpCode.HTTP_NOT_FOUND);
                }

                req.router = new self(route, webModule, req, res, {
                    controllerPath: C.urlJoin(options.controllerPath, actionInfo.controller),
                    modelPath: options.modelPath,
                    action: actionInfo.action,
                    view: actionInfo.view
                });

                next();
            } catch (error) {
                Router.onError(webModule, req, res, error);
            }
        }

        Router.getMiddlewares(webModule, route, options.middlewares, function (err, middlewares) {
            if (err) return cb(err);

            middlewares.unshift(createRouter);

            var routerRules = [];

            function applyMiddlewares(callback) {
                C.async.eachSeries(routerRules, function (routerRule, cb2) {
                    routerRule.method.apply(engine, [routerRule.mount].concat(middlewares, routerRule.middlewares));
                    cb2();
                }, callback);
            }

            C.async.eachSeries(C._.keys(options.rules), function (rule, processRuleComplete) {
                var httpMethods = options.rules[rule];

                if (rule.charAt(0) === '_' || rule.indexOf('/_') !== -1) {
                    return processRuleComplete('"_" cannot be used the leading character of a path node.');
                }

                var pos = rule.indexOf(':/');

                if (pos > 0) {
                    var embeddedMethod = rule.substr(0, pos);
                    rule = rule.substr(pos + 2);

                    var tmp = {};
                    tmp[embeddedMethod] = httpMethods;
                    httpMethods = tmp;
                }

                if (rule.indexOf(':') !== -1) {
                    return processRuleComplete('Invalid rule syntax: unexpected ":" found!');
                }

                routeRule = C.trimRightSlash(C.urlJoin(route, rule));
                if (routeRule === '') routeRule = '/';

                if (C._.isString(httpMethods)) {
                    httpMethods = { all: httpMethods };
                }

                C.async.eachSeries(C._.keys(httpMethods), function (httpMethod, processMethodComplete) {
                    var actionsInfo = httpMethods[httpMethod];

                    var routerFun = engine[httpMethod];
                    if (typeof routerFun !== 'function') {
                        return processMethodComplete('Invalid http method: ' + httpMethod);
                    }

                    if (C._.isString(actionsInfo)) {
                        actionsInfo = { action: actionsInfo };
                    }

                    var extraMiddlewares = [];

                    var middlewareLib = require('../features/middlewares.js');

                    C.async.eachSeries(C._.keys(actionsInfo), function (name, createMiddlwareComplete) {

                        middlewareLib.createMiddleware(webModule, httpMethod + ':' + routeRule, name, actionsInfo[name], function (err, middleware) {
                            if (err) return createMiddlwareComplete(err);

                            extraMiddlewares.push(middleware);
                            createMiddlwareComplete();
                        });
                    }, function (err) {
                        if (err) return processMethodComplete(err);

                        routerRules.push({ method: routerFun, mount: routeRule, middlewares: extraMiddlewares });
                        processMethodComplete();
                    });
                }, function (err) {
                    if (err) return processRuleComplete(err);

                    applyMiddlewares(processRuleComplete);
                });
            }, cb);
        });
    }
});

module.exports = RuleRouter;